﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Globalization;
using System.ComponentModel;
using System.Collections;

namespace Enterprise.Utilities
{
    public static class ReflectionHelper
    {
        // Methods
        

        public static D CoerceValue<D>(Type valueType, object oldValue, object value)
        {
            return (D)CoerceValue(typeof(D), valueType, oldValue, value);
        }

        public static object CoerceValue(Type desiredType, Type valueType, object oldValue, object value)
        {
            if (desiredType.Equals(valueType))
            {
                return value;
            }
            if (desiredType.IsGenericType && (desiredType.GetGenericTypeDefinition() == typeof(Nullable<>)))
            {
                if (value == null)
                {
                    return null;
                }
                if (valueType.Equals(typeof(string)) && string.IsNullOrEmpty(Convert.ToString(value, CultureInfo.InvariantCulture)))
                {
                    return null;
                }
            }
            desiredType = GetPropertyType(desiredType);
            if (desiredType.IsEnum && (valueType.Equals(typeof(string)) || Enum.GetUnderlyingType(desiredType).Equals(valueType)))
            {
                return Enum.Parse(desiredType, value.ToString());
            }
            if ((desiredType.IsPrimitive || desiredType.Equals(typeof(decimal))) && (valueType.Equals(typeof(string)) && string.IsNullOrEmpty((string)value)))
            {
                value = 0;
            }
            try
            {
                return Convert.ChangeType(value, desiredType, CultureInfo.InvariantCulture);
            }
            catch
            {
                TypeConverter converter = TypeDescriptor.GetConverter(desiredType);
                if ((converter == null) || !converter.CanConvertFrom(valueType))
                {
                    throw;
                }
                return converter.ConvertFrom(value);
            }
        }

        public static object ConvertType(object value, Type type)
        {
            string text = value as string;
            if (text != null)
            {
                TypeConverter converter = TypeDescriptor.GetConverter(type);
                if (converter == null)
                {
                    return value;
                }
                try
                {
                    value = converter.ConvertFromString(text);
                }
                catch (NotSupportedException)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Cannot convert from {0} to {1}.", new object[] { typeof(string).FullName, type.FullName }));
                }
                catch (FormatException)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Cannot convert from {0} to {1}.", new object[] { typeof(string).FullName, type.FullName }));
                }
            }
            return value;
        }

        public static List<object> Flatten(object target)
        {
            if (target == null)
            {
                throw new ArgumentNullException("target");
            }
            List<object> hierarchy = new List<object>();
            Flatten(target, hierarchy);
            return hierarchy;
        }

        private static void Flatten(object target, List<object> hierarchy)
        {
            Type type = target.GetType();
            BindingFlags bindingAttr = BindingFlags.Public | BindingFlags.Instance;
            foreach (PropertyInfo info in type.GetProperties(bindingAttr).Where<PropertyInfo>(delegate(PropertyInfo p)
            {
                return IsCollection(p.GetValue(target, null));
            }))
            {
                object obj2 = info.GetValue(target, null);
                if (obj2 != null)
                {
                    foreach (object obj3 in obj2 as IEnumerable)
                    {
                        if (obj3 != null)
                        {
                            hierarchy.Add(obj3);
                            Flatten(obj3, hierarchy);
                        }
                    }
                    continue;
                }
            }
        }

        public static Type GetPropertyType(Type propertyType)
        {
            Type nullableType = propertyType;
            if (nullableType.IsGenericType && (nullableType.GetGenericTypeDefinition() == typeof(Nullable<>)))
            {
                return Nullable.GetUnderlyingType(nullableType);
            }
            return nullableType;
        }

        public static object GetPropertyValue(object holder, string propertyName)
        {
            if (holder == null)
            {
                throw new ArgumentNullException("holder");
            }
            if (string.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentNullException("propertyName");
            }
            PropertyInfo property = holder.GetType().GetProperty(propertyName);
            Type propertyType = property.PropertyType;
            return ConvertType(property.GetValue(holder, null), propertyType);
        }

        public static bool IsCollection(object target)
        {
            if (target == null)
            {
                return false;
            }
            Type[] interfaces = target.GetType().GetInterfaces();
            Type type = target.GetType();
            return ((interfaces.Contains<Type>(typeof(IEnumerable)) && (type != typeof(string))) && (type != typeof(string)));
        }

        //public static bool IsEditable<TEntity, TIdentifier>()
        //{
        //    return typeof(TEntity).GetInterfaces().Contains<Type>(typeof(IEditableEntity<TIdentifier>));
        //}

        //public static bool IsReadOnly<TEntity, TIdentifier>()
        //{
        //    return typeof(TEntity).GetInterfaces().Contains<Type>(typeof(IReadOnlyEntity<TIdentifier>));
        //}

        public static void SetPropertyValue(object holder, string propertyName, object propertyValue)
        {
            if (holder == null)
            {
                throw new ArgumentNullException("holder");
            }
            if (string.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentNullException("propertyName");
            }
            PropertyInfo property = holder.GetType().GetProperty(propertyName);
            Type propertyType = property.PropertyType;
            property.SetValue(holder, ConvertType(propertyValue, propertyType), null);
        }
    }


}
